//// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
//package com.linqingying.gitcode.pullrequest.data
//
//import com.intellij.collaboration.async.cancelledWith
//import com.intellij.collaboration.async.classAsCoroutineName
//import com.intellij.openapi.Disposable
//import com.intellij.openapi.project.Project
//import com.intellij.openapi.util.CheckedDisposable
//import com.intellij.openapi.util.Disposer
//import com.intellij.platform.util.coroutines.childScope
//import com.intellij.util.EventDispatcher
//import com.intellij.util.asSafely
//import com.intellij.util.concurrency.annotations.RequiresEdt
//import com.intellij.util.messages.ListenerDescriptor
//import com.intellij.util.messages.MessageBusFactory
//import com.intellij.util.messages.MessageBusOwner
//import com.intellij.vcs.log.data.DataPackChangeListener
//import com.intellij.vcs.log.impl.VcsProjectLog
//import kotlinx.coroutines.CoroutineScope
//import com.linqingying.gitcode.api.data.GCIssueComment
//import com.linqingying.gitcode.api.data.pullrequest.GCPullRequest
//import com.linqingying.gitcode.api.data.pullrequest.GCPullRequestReview
//import com.linqingying.gitcode.api.data.pullrequest.timeline.GCPRTimelineItem
//import com.linqingying.gitcode.pullrequest.data.provider.*
//import com.linqingying.gitcode.pullrequest.data.service.*
//import com.linqingying.gitcode.util.DisposalCountingHolder
//import java.util.*
//
//internal class GCPRDataProviderRepositoryImpl(private val project: Project,
//                                              parentCs: CoroutineScope,
//                                              private val repositoryDataService: GCPRRepositoryDataService,
//                                              private val detailsService: GCPRDetailsService,
//                                              private val stateService: GCPRStateService,
//                                              private val reviewService: GCPRReviewService,
//                                              private val filesService: GCPRFilesService,
//                                              private val commentService: GCPRCommentService,
//                                              private val changesService: GCPRChangesService,
//                                              private val timelineLoaderFactory: (GCPRIdentifier) -> GCListLoader<GCPRTimelineItem>)
//  : GCPRDataProviderRepository {
//  private val cs = parentCs.childScope(classAsCoroutineName())
//
//  private var isDisposed = false
//
//  private val cache = mutableMapOf<GCPRIdentifier, DisposalCountingHolder<GCPRDataProvider>>()
//  private val providerDetailsLoadedEventDispatcher = EventDispatcher.create(DetailsLoadedListener::class.java)
//
//  init {
//    Disposer.register(this, changesService)
//  }
//
//  @RequiresEdt
//  override fun getDataProvider(id: GCPRIdentifier, disposable: Disposable): GCPRDataProvider {
//    if (isDisposed) throw IllegalStateException("Already disposed")
//
//    return cache.getOrPut(id) {
//      DisposalCountingHolder {
//        createDataProvider(it, id)
//      }.also {
//        Disposer.register(it, Disposable { cache.remove(id) })
//      }
//    }.acquireValue(disposable)
//  }
//
//  @RequiresEdt
//  override fun findDataProvider(id: GCPRIdentifier): GCPRDataProvider? = cache[id]?.value
//
//  override fun dispose() {
//    isDisposed = true
//    cache.values.toList().forEach(Disposer::dispose)
//  }
//
//  private fun createDataProvider(parentDisposable: CheckedDisposable, id: GCPRIdentifier): GCPRDataProvider {
//    val providerCs = cs.childScope(classAsCoroutineName<GCPRDataProviderImpl>()).apply {
//      cancelledWith(parentDisposable)
//    }
//    val messageBus = MessageBusFactory.newMessageBus(object : MessageBusOwner {
//      override fun isDisposed() = parentDisposable.isDisposed
//
//      override fun createListener(descriptor: ListenerDescriptor) =
//        throw UnsupportedOperationException()
//    })
//    Disposer.register(parentDisposable, messageBus)
//
//    val detailsData = GCPRDetailsDataProviderImpl(detailsService, id, messageBus).apply {
//      addDetailsLoadedListener(parentDisposable) {
//        loadedDetails?.let { providerDetailsLoadedEventDispatcher.multicaster.onDetailsLoaded(it) }
//      }
//    }.also {
//      Disposer.register(parentDisposable, it)
//    }
//
//    VcsProjectLog.runWhenLogIsReady(project) {
//      if (!parentDisposable.isDisposed) {
//        val dataPackListener = DataPackChangeListener {
//          detailsData.reloadDetails()
//        }
//
//        it.dataManager.addDataPackChangeListener(dataPackListener)
//        Disposer.register(parentDisposable, Disposable {
//          it.dataManager.removeDataPackChangeListener(dataPackListener)
//        })
//      }
//    }
//
//    val stateData = GCPRStateDataProviderImpl(stateService, id, messageBus, detailsData).also {
//      Disposer.register(parentDisposable, it)
//    }
//    val changesData = GCPRChangesDataProviderImpl(providerCs, repositoryDataService, changesService, id, detailsData).also {
//      Disposer.register(parentDisposable, it)
//    }
//    val reviewData = GCPRReviewDataProviderImpl(reviewService, changesData, id, messageBus).also {
//      Disposer.register(parentDisposable, it)
//    }
//    val viewedStateData = GCPRViewedStateDataProviderImpl(filesService, id).also {
//      Disposer.register(parentDisposable, it)
//    }
//    val commentsData = GCPRCommentsDataProviderImpl(commentService, id, messageBus)
//
//    val timelineLoaderHolder = DisposalCountingHolder { timelineDisposable ->
//      timelineLoaderFactory(id).also { loader ->
//        messageBus.connect(timelineDisposable).subscribe(GCPRDataOperationsListener.TOPIC, object : GCPRDataOperationsListener {
//          override fun onStateChanged() = loader.loadMore(true)
//          override fun onMetadataChanged() = loader.loadMore(true)
//
//          override fun onCommentAdded() = loader.loadMore(true)
//          override fun onCommentUpdated(commentId: String, newBody: String) {
//            loader.updateData { item ->
//              item.asSafely<GCIssueComment>()?.takeIf { it.id == commentId }?.copy(body = newBody)
//            }
//            loader.loadMore(true)
//          }
//
//          override fun onCommentDeleted(commentId: String) {
//            loader.removeData { it is GCIssueComment && it.id == commentId }
//            loader.loadMore(true)
//          }
//
//          override fun onReviewsChanged() = loader.loadMore(true)
//
//          override fun onReviewUpdated(reviewId: String, newBody: String) {
//            loader.updateData { item ->
//              item.asSafely<GCPullRequestReview>()?.takeIf { it.id == reviewId }?.copy(body = newBody)
//            }
//            loader.loadMore(true)
//          }
//        })
//        Disposer.register(timelineDisposable, loader)
//      }
//    }.also {
//      Disposer.register(parentDisposable, it)
//    }
//
//    messageBus.connect(stateData).subscribe(GCPRDataOperationsListener.TOPIC, object : GCPRDataOperationsListener {
//      override fun onReviewsChanged() {
//        stateData.reloadMergeabilityState()
//        // TODO: check if we really need it
//        detailsData.reloadDetails()
//      }
//    })
//
//    return GCPRDataProviderImpl(
//      id, detailsData, stateData, changesData, commentsData, reviewData, viewedStateData, timelineLoaderHolder
//    )
//  }
//
//  override fun addDetailsLoadedListener(disposable: Disposable, listener: (GCPullRequest) -> Unit) {
//    providerDetailsLoadedEventDispatcher.addListener(object : DetailsLoadedListener {
//      override fun onDetailsLoaded(details: GCPullRequest) {
//        listener(details)
//      }
//    }, disposable)
//  }
//
//  private interface DetailsLoadedListener : EventListener {
//    fun onDetailsLoaded(details: GCPullRequest)
//  }
//}
